<!-- author: xuett -->
<!-- date: 2025-06-09 14:48:48 -->
<!-- description: 机柜 -->
<template>
  <div>
    <div ref="container" class="container" @click="selectCabinet">
      <div class="plane" :style="{ left: planePos.left, top: planePos.top, display: planePos.hasDisplay ? 'block' : 'none' }">
        <div>设备名称：{{ curRackInfo.name }}</div>
        <div>设备编号：{{ curRackInfo.serialNumber }}</div>
        <div>设备类型：{{ curRackInfo.deviceType ? deviceType[curRackInfo.deviceType - 1] : "" }}</div>
      </div>
    </div>
  </div>
</template>

<script setup>
import * as THREE from "three";
import Viewer from "@/hooks/viewer.js";
import postprocessing from "@/hooks/postprocessing.js";
import { createCabinetBox } from "@/utils/createCabinets";
import TWEEN from "@tweenjs/tween.js";
import { Reflector } from "three/examples/jsm/objects/Reflector.js";

const deviceType = ["管理单元", "交换机", "配线架"];
let viewerIns = null,
  postprocessingIns = null;

//鼠标点击的机柜设备
var curDevice = null;

const container = ref(null);
const state = reactive({
  showPostProcessing: true,
  // 信息框位置
  planePos: {
    hasDisplay: false,
    left: 0,
    top: 0
  },
  //选中机柜信息
  curRackInfo: {}
});
const { planePos, curRackInfo } = toRefs(state);

onMounted(() => {
  init();
});

var animationId;
onUnmounted(() => {
  // 清理资源
  cancelAnimationFrame(animationId);
  if (viewerIns) {
    viewerIns.dispose();
  }
});

const init = async () => {
  viewerIns = new Viewer(container);
  // 创建后处理效果
  if (state.showPostProcessing) {
    postprocessingIns = new postprocessing(viewerIns, container);
  }

  createPlane();

  const { boxgroup, deviceList } = createCabinetBox(42); //U数
  viewerIns.scene.add(boxgroup);
  viewerIns.setRaycasterObjects(deviceList);

  // 处理窗口大小调整
  window.addEventListener("resize", () => onWindowResize());
  viewerIns.renderer.autoClear = false;
  animate();
};

const createPlane = () => {
  let planeGroup = new THREE.Group();

  const circle1 = new THREE.Mesh(
    new THREE.CircleGeometry(5.1, 64),
    new THREE.MeshBasicMaterial({
      color: "#5E81F1",
      transparent: true,
      opacity: 0.5
    })
  );
  circle1.rotation.x = -Math.PI / 2;
  circle1.position.y = 0.05;

  const edges1 = new THREE.EdgesGeometry(circle1.geometry);
  const line1 = new THREE.LineSegments(edges1, new THREE.LineBasicMaterial({ color: "#5E81F1" }));
  line1.layers.set(1);
  circle1.add(line1);
  planeGroup.add(circle1);

  const circle2 = new THREE.Mesh(new THREE.CircleGeometry(8, 64), new THREE.MeshBasicMaterial({ color: "#19243E", transparent: true, opacity: 0.5 }));
  circle2.rotation.x = -Math.PI / 2;

  const edges2 = new THREE.EdgesGeometry(circle2.geometry);
  const line2 = new THREE.LineSegments(edges2, new THREE.LineBasicMaterial({ color: "#7C93C9" }));
  line2.layers.set(1);
  circle2.add(line2);
  planeGroup.add(circle2);

  // 创建反射器（镜面地板）
  const reflectorGeometry = new THREE.CircleGeometry(5, 64);
  const reflector = new Reflector(reflectorGeometry, {
    textureWidth: 512,
    textureHeight: 512,
    color: "#888888",
    clipBias: 0.003 // 裁剪偏移量，用于避免反射表面与自身相交产生的视觉问题
  });
  reflector.rotation.x = -Math.PI / 2;
  reflector.position.y = 0.01;
  planeGroup.add(reflector);

  viewerIns.scene.add(planeGroup);
};

// 窗口大小调整处理函数
const onWindowResize = () => {
  const width = container.value.clientWidth;
  const height = container.value.clientHeight;
  viewerIns.resize(width, height);
  postprocessingIns.resize(width, height);
};

// 动画循环
const animate = () => {
  // 使用后处理合成器渲染场景
  // if (state.showPostProcessing) {
  //   postprocessingIns.render();
  //   viewerIns.controls.update();
  // } else {
  //   //不使用后处理，直接渲染场景
  //   viewerIns.render();
  // }

  viewerIns.renderer.clear();
  viewerIns.camera.layers.set(1);
  postprocessingIns.composer.render();

  viewerIns.renderer.clearDepth();
  viewerIns.camera.layers.set(0);
  viewerIns.render();

  animationId = requestAnimationFrame(animate);
  TWEEN.update();
};

const selectCabinet = (mouseEvent) => {
  let intersectObj = viewerIns.getRaycasterIntersectObjects(mouseEvent);
  if (intersectObj) {
    // 切换该对象的高亮状态
    if (state.showPostProcessing) {
      postprocessingIns.toggleOutline(intersectObj);
    }
    if (intersectObj !== curDevice) {
      selectObject(intersectObj, mouseEvent);
    }
  } else if (curDevice) {
    deselectObject();
  }
};

const selectObject = (object, mouseEvent) => {
  // 取消之前选中的对象
  deselectObject();
  curDevice = object;

  //选中模型突出，z轴偏移
  object.userData.originPosition = object.position.clone();
  const tween = new TWEEN.Tween(object.position);
  tween.to({ z: object.position.z + 2 }, 500).start();

  // 创建线框材质
  const wireframeMaterial = new THREE.LineBasicMaterial({
    color: 0xffffff,
    linewidth: 2
  });
  // 创建线框几何体
  const edges = new THREE.EdgesGeometry(object.geometry);
  const wireframe = new THREE.LineSegments(edges, wireframeMaterial);
  // 将线框添加到选中模型
  object.add(wireframe);

  //显示信息窗
  state.curRackInfo = object.userData;
  state.planePos.left = mouseEvent.clientX + "px";
  state.planePos.top = mouseEvent.clientY + "px";
  state.planePos.hasDisplay = Object.keys(object.userData).length ? true : false;
};

const deselectObject = () => {
  if (curDevice) {
    const tween = new TWEEN.Tween(curDevice.position);
    let copyP = curDevice.userData.originPosition;
    tween.to({ x: copyP.x, y: copyP.y, z: copyP.z }, 500);
    tween.start();
    let index = curDevice.children.findIndex((item) => item.type == "LineSegments");
    if (index > -1) {
      curDevice.children[index].material.dispose();
      curDevice.remove(curDevice.children[index]);
    }
    curDevice = null;
  }
  state.planePos.hasDisplay = false;
};
</script>

<style>
.container {
  position: absolute;
  width: 100%;
  height: 100%;
  .plane {
    position: absolute;
    top: 0;
    left: 0;
    background-color: rgba(0, 0, 0, 0.6);
    padding: 0 18px;
    transform: translate(12px, -100%);
    display: none;
    color: #fff;
    font-size: 16px;
    font-weight: normal;
    user-select: none;
  }
}
</style>
